#include "rgb.h"
//#include "ultrasonic.h"
/***********************************************************************
func: 将type指定的类型(数据宽度)的data数据保存到addr映射的虚拟地址空间中
	  物理地址不能直接修改，必须进行映射
	  根据数据手册的说明,使用gpio的PWM功能需要配置PWM控制器和对应的pwm的引脚（即gpio0_13
	  和gpio1_1）
param: addr 传入的PWM控制器的物理地址（0x2807e020）
	   type 访问内存的类型 字(W) 半字(H) 字节(B)
	   data 要写入虚拟地址空间中的数据（0xff）
************************************************************************/
int PWM_gpio_mode (char *addr, char *type, char *data)
{
	int fd;
	int type_access = 'w';
	off_t target;
	void *map_base, *virt_addr;
	unsigned long writeval;
	if (0 > (fd = open ("/dev/mem", O_ASYNC | O_RDWR))){
		perror ("open /dev/mem error!");
		exit (-1);
	}
	//strtoul:字符串转换成无符号长整型
	target = strtoul (addr, 0, 0);
	//tolower:将大写字母转还为小写字母
	type_access = tolower (type[0]);
	writeval = strtoul (data, 0, 0);
	//映射虚拟地址
	//target & ~MAP_MASK 地址对齐
	map_base = mmap (0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~MAP_MASK);
	if (map_base == (void *)(-1)) {
		perror ("mmap");
		return -1;
	}
	//计算映射之后的虚拟地址
	virt_addr= map_base + (target & MAP_MASK);
	switch (type_access) {
		case 'b':
			*((unsigned char *) virt_addr) = writeval;
		break;
		case 'h':
			*((unsigned short *) virt_addr) = writeval;
		break;
		case 'w':
			*((unsigned int *) virt_addr) = writeval;
		break;
	}
	if (-1 == munmap (map_base, MAP_SIZE))
	{
		perror ("munmap error!");
		return -1;
	}
	close (fd);
	return 0;
}

//将pwm0和pwm2的节点文件导出到用户空间
//注意这个函数应当在pwm_config()和set_pwm_value()前调用
void set_pwm_export (void)
{
	sprintf (pwm0_path, "/sys/class/pwm/pwmchip%s/pwm%s", PWM0_CONTROLLER, PWM0_CHANNEL0);
	sprintf (pwm2_path, "/sys/class/pwm/pwmchip%s/pwm%s", PWM2_CONTROLLER, PWM2_CHANNEL0);

	if (access(pwm0_path, F_OK)) {
		int fd = 0;
		int len = 0;
		if (0 > (fd = open ("/sys/class/pwm/pwmchip0/export", O_WRONLY))) {
			perror ("open");
			exit (-1);
		}
		len = strlen (PWM0_CHANNEL0);
		if (len != write (fd, PWM0_CHANNEL0, len)) {
			perror ("write error");
			close (fd);
			exit (-1);
		}	
	}
	if (access (pwm2_path, F_OK)) {
		int fd = 0;
		int len = 0;
		if (0 > (fd = open ("/sys/class/pwm/pwmchip2/export", O_WRONLY))) {
			perror ("open");
			exit (-1);
		}
		//注意还是用channel0!
		len = strlen (PWM0_CHANNEL0);
		if (len != write (fd, PWM0_CHANNEL0, len))
		{
			perror ("write error");
			close (fd);
			exit (-1);
		}
	}
}

/*********************************************************
func:将设备节点文件导出到用户空间并配置pwm下的节点文件的属性
param:
	attr:pwm period,duty_cycle,enable
	val:
	node:pwm0或pwm2
************************************************************/
int pwm_config (const char *attr, const char *val, int node)
{
	//保存属性文件的路径
	char file_path[256];
	int fd, len;

	//配置pwm下的节点文件的属性文件
	if (node == 0) {
		if ((strlen (pwm0_path) + strlen (attr)) > (sizeof(file_path)/sizeof(char))) {
			fprintf (stderr, "file path is too loong!");
			return -1;
		}
		sprintf (file_path, "%s/%s", pwm0_path, attr);
	}else if (node == 2) {
		if ((strlen (pwm2_path) + strlen (attr)) > (sizeof(file_path)/sizeof(char))) {
			fprintf (stderr, "file path is too loong!");
			return -1;
		}
		sprintf (file_path, "%s/%s", pwm2_path, attr);
	}
	if (0 > (fd = open (file_path, O_WRONLY))) {
		perror ("open attr file error!");
		close (fd);
		return -1;
	}
	len = strlen (val);
	if (len != write (fd, val, len)) {
		perror ("write attr file error!");
		close (fd);
		return -1;
	}
	//usleep(50000);
	close (fd);
	return 0;
}

//配置pwm period,duty_cycle,enable
void set_pwm_value (void)
{
	//配置第0路pwm
	//a) 配置pwm的持续周期1ms
	if (pwm_config ("period", PWM_PERIOD, 0) < 0) {
		fprintf (stderr, "config pwm0 period attribute failed!");
		exit (-1);
	}
	//b) 配置占空比为0 
	if (pwm_config ("duty_cycle", PWM_DUTY1, 0) < 0) {
		fprintf (stderr, "config pwm0 duty attribute failed!");
		exit (-1);
	}
	//c) 使能pwm控制器
	if (pwm_config ("enable", "1", 0) < 0) {
		fprintf (stderr, "config pwm0 enable attribute failed!");
		exit (-1);
	}
	//配置第2路pwm
	//a) 配置pwm的持续周期1ms
	if (pwm_config ("period", PWM_PERIOD, 2) < 0) {
		fprintf (stderr, "config pwm2 duty attribute failed!");
		exit (-1);
	}
	//b) 配置占空比为0
	if (pwm_config ("duty_cycle", PWM_DUTY1, 2) < 0) {
		fprintf (stderr, "config pwm2 duty attribute failed!");
		exit (-1);
	}
	//c) 使能pwm控制器
	if (pwm_config ("enable", "1", 2) < 0) {
		fprintf (stderr, "config pwm2 enable attribute failed!");
		exit (-1);
	}
}
//灯光初始化(关闭灯光)
void rgb_init (void)
{
	if (0 > pwm_config ("duty_cycle", "0", 2)) {
		fprintf (stderr, "config pwm2 duty cycle error");
		exit (-1);
	}
	if (0 > pwm_config ("duty_cycle", "0", 0)) {
		fprintf (stderr, "config pwm2 duty cycle error");
		exit (-1);
	}
}

//红蓝闪烁模式
void red_blue_mode (void)
{
	//设置一个标志位，终止while(1)的执行
	//while (1) {
		//pwm0常亮，pwm2不亮
		if (0 > pwm_config ("duty_cycle", PWM_DUTY1, 2)) {
			fprintf (stderr, "config pwm2_r_b_m duty cycle error");
			exit (-1);
		}
		if (0 > pwm_config ("duty_cycle", "1000", 0)) {
			fprintf (stderr, "config pwm0_r_b_m duty cycle error");
			exit (-1);
		}
		usleep(100000);
		//pwm0不亮，pwm2常量
		if (0 > pwm_config ("duty_cycle", PWM_DUTY1, 0)) {
			fprintf (stderr, "config pwm0_r_b_m duty cycle error");
			exit (-1);
		}
		if (0 > pwm_config ("duty_cycle", "1000", 2)) {
			fprintf (stderr, "config pwm2_r_b_m duty cycle error");
			exit (-1);
		}
		usleep(10000);
	//}
}
//呼吸灯模式
void breath_mode (void)
{
	/*
	//设置一个线程
	unsigned long duty1 = atol(PWM_DUTY1);//0
	unsigned long duty2 = atol(PWM_DUTY2);//1000000
	unsigned long duty = duty1;//0
	int increase = 1;
	int step;
	char dutys[128];

	//定义调节占空比的步长
	step = (int)(duty2 - duty1) / 1000;//1000
	printf ("duty1 = %ld, duty2 = %ld, step = %d\n", duty1, duty2, step);
	*/
	//设置一个标志位，终止while(1)的执行
	//while (1) {
	
	
		printf ("dutys:%s\n", dutys);//test
		printf ("duty:%d\n", duty);//test
		printf ("step:%d\n", step);
		
		if (increase == 1) {
			duty += step;
		} else if (increase == 0) {
			duty -= step;
		}

		memset (dutys, 0, sizeof(dutys) / sizeof(char));
		sprintf (dutys, "%d", duty);
		
		printf ("dutys:%s\n", dutys);//test
		printf ("duty:%d\n", duty);//test
		
		
		//如果用户选择的是pwm2和pwm0, 这两路同时改变占空比
		if (0 > pwm_config ("duty_cycle", dutys, 0)) {
			fprintf (stderr, "config pwm0_b_m duty cycle error");
			exit (-1);
		}
		if (0 > pwm_config ("duty_cycle", dutys, 2)) {
			fprintf (stderr, "config pwm2_b_m duty cycle error");
			exit (-1);
		}
		if (duty >= duty2) {
			increase = 0;
		}
		if (duty <= 100) {
			increase = 1;
		}
		printf ("increase:%d\n",increase);
		usleep (100000);
	//}
}
